[REFACTOR] 깃허브 토큰이 레포지토리 접근 권한 없을 때의 에러코드 구분#78
Conversation
WalkthroughRepositoryFacadeService.save now catches GithubNotFoundException and rethrows GssException with ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND. A corresponding error code was added to ErrorCode enum. Tests were added to verify exception translation on webhook creation failure. The PR template was simplified by removing Jira-specific sections. Changes
Sequence Diagram(s)sequenceDiagram
actor Client
participant Facade as RepositoryFacadeService
participant GH as GitHubClient
participant Repo as RepositoryService
participant Pub as EventPublisher
Client->>Facade: save(request)
Facade->>GH: getRepositoryInfo()
GH-->>Facade: RepoInfo
Facade->>GH: createWebhook()
alt Webhook created
Facade->>Repo: createRepository()
Repo-->>Facade: Repository
Facade->>Pub: publishRegisteredEvent()
Pub-->>Facade: ack
Facade-->>Client: success
else GithubNotFoundException
Facade-->>Client: throw GssException(REGISTRY_GITHUB_REPOSITORY_NOT_FOUND)
end
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
Test Results0 tests 0 ✅ 0s ⏱️ Results for commit 43922f8. |
📝 Test Coverage Report
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (6)
.github/pull_request_template.md (1)
1-4: Jira 의존성 제거는 좋습니다. GitHub 이슈 자동 연결 문구를 안내로 추가 제안조직 도구에 중립적인 템플릿으로 간결해졌습니다. 다만 기여자들이 PR 머지 시 이슈가 자동으로 닫히도록 “Closes #번호” 형태의 안내를 한 줄 넣어두면 실수 방지에 도움 됩니다.
적용 예시(해당 블록 중간에 한 줄 안내 추가):
# 🚩 연관 이슈 + +- GitHub 이슈 예: Closes #123 # 🔂 변경 내역gss-common/src/main/java/com/devoops/exception/errorcode/ErrorCode.java (1)
9-11: 이름 충돌(혼동) 가능성: NOT_FOUND 네이밍이 4xx/5xx에 혼재REGISTRY_GITHUB_REPOSITORY_NOT_FOUND(400)와 GITHUB_REPOSITORY_NOT_FOUND(500)이 공존해 의미가 모호해질 수 있습니다. 현재 PR 의도는 “GitHub 404를 400으로 변환”인데, 식별자에 NOT_FOUND가 들어가면 404를 연상시키는 여지가 있습니다. 운영상 혼선을 줄이려면:
- 주석/Javadoc로 용례를 명확히 기록, 또는
- REGISTRY_GITHUB_REPOSITORY_REGISTRATION_FAILED처럼 결과(등록 실패) 중심 네이밍 고려
메시지는 그대로 두고 식별자만 구체화하는 것도 방법입니다.
Also applies to: 29-29
gss-api-app/src/main/java/com/devoops/service/facade/RepositoryFacadeService.java (1)
34-45: 트랜잭션 경계 내 외부 I/O 호출 최소화 제안현재 save 트랜잭션 내에서 웹훅 등록(외부 네트워크 I/O)과 이벤트 발행이 수행됩니다. 타임아웃/재시도 시 장기 트랜잭션이 될 수 있어 DB 락 유지 시간이 늘어날 우려가 있습니다. 선택지:
- 웹훅 등록을 트랜잭션 밖에서 수행(실패 시 보상/정리 로직 포함)
- outbox/event-relay 패턴 도입으로 이벤트 발행 비동기화
gss-api-app/src/test/java/com/devoops/service/facade/RepositoryFacadeServiceTest.java (3)
94-102: 불필요한 지역변수 제거mockingErrorWhenCreateWebHook 내 WebHookCreateResponse 로컬 변수는 사용되지 않습니다. 제거해 주세요.
적용 diff:
private void mockingErrorWhenCreateWebHook() { GithubRepoInfoResponse mockResponse = new GithubRepoInfoResponse(123, "testName", "testUrl", new OwnerResponse("김건우")); - WebHookCreateResponse mockWebHookCreateResponse = new WebHookCreateResponse(123); Mockito.when(gitHubClient.getRepositoryInfo(anyString(), anyString(), anyString())) .thenReturn(mockResponse); Mockito.when(gitHubClient.createWebhook(any(), any(), any(), any())) .thenThrow(new GithubNotFoundException("mocking error")); }
60-69: 레포지토리 정보 조회 단계 실패 케이스 추가 테스트 제안현재는 “웹훅 등록” 단계의 404만 커버합니다. GitHub 레포지토리 정보 조회(getRepositoryInfo) 단계에서의 404도 동일하게 변환되는지 보강 테스트를 권장합니다.
예시 테스트(새 메서드 추가):
@Test void 레포지토리_정보_조회_404_시_애플리케이션_에러로_전환한다() { User user = userGenerator.generate("김건우"); RepositorySaveRequest request = new RepositorySaveRequest("https://github.com/octocat/Hello-World"); // getRepositoryInfo가 404를 유발하도록 설정 Mockito.when(gitHubClient.getRepositoryInfo(anyString(), anyString(), anyString())) .thenThrow(new GithubNotFoundException("mocking error")); assertThatThrownBy(() -> repositoryFacadeService.save(request, user)) .isInstanceOf(GssException.class) // 가능하면 에러코드 단정으로 교체 (앞선 코멘트 참고) .hasMessage(ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND.getMessage()); // 단락 평가 검증: 웹훅 생성은 시도되지 않아야 함 Mockito.verify(gitHubClient, times(0)).createWebhook(any(), any(), any(), any()); }원하시면 제가 바로 추가 PR 패치까지 준비하겠습니다.
60-69: 예외 메시지 대신 에러 코드로 단정 검증 권장현재 테스트는 메시지 문자열(
.hasMessage(...))에 의존해 있어, 국제화나 문구 변경 시 깨질 위험이 있습니다.
GssException에는 Lombok@Getter로 생성된getErrorCode()접근자가 있으므로, 메시지 대신 에러 코드를 검증하도록 리팩토링하는 것이 더 견고합니다.예시 변경안:
assertThatThrownBy(() -> repositoryFacadeService.save(request, user)) .isInstanceOf(GssException.class) .satisfies(ex -> assertThat(((GssException) ex).getErrorCode()) .isEqualTo(ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND));
- 파일: gss-api-app/src/test/java/com/devoops/service/facade/RepositoryFacadeServiceTest.java
- 위치: 테스트 메소드 내
assertThatThrownBy호출부
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
.github/pull_request_template.md(1 hunks)gss-api-app/src/main/java/com/devoops/service/facade/RepositoryFacadeService.java(2 hunks)gss-api-app/src/test/java/com/devoops/service/facade/RepositoryFacadeServiceTest.java(2 hunks)gss-common/src/main/java/com/devoops/exception/errorcode/ErrorCode.java(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
gss-api-app/src/main/java/com/devoops/service/facade/RepositoryFacadeService.java (1)
gss-client/gss-github-client/src/main/java/com/devoops/exception/GithubNotFoundException.java (1)
GithubNotFoundException(3-7)
🔇 Additional comments (3)
gss-common/src/main/java/com/devoops/exception/errorcode/ErrorCode.java (2)
9-9: 새 에러코드 추가 방향성 적절GitHub의 404 응답을 사용자 입력 오류(권한 미보유/존재하지 않는 URL)로 번역해 400 계열로 노출하려는 의도가 명확합니다. 메시지도 사용자 친화적입니다.
9-9: [예외 처리기 확인] ErrorCode.statusCode가 HTTP 응답 상태로 정상 매핑됨
- GlobalExceptionHandler.java (라인 88)에서
ResponseEntity.status(errorCode.getStatusCode())를 사용하여
ErrorCode.statusCode(예: 400)가 그대로 HTTP 상태 코드로 설정됩니다.- 따라서
REGISTRY_GITHUB_REPOSITORY_NOT_FOUND예외 발생 시 400 상태가 올바르게 반영됩니다.추가 검증이나 수정은 필요하지 않습니다.
gss-api-app/src/main/java/com/devoops/service/facade/RepositoryFacadeService.java (1)
36-44: GithubNotFoundException → 도메인 예외 변환 방향성 적절웹훅 등록/레포 조회 단계에서의 GitHub 404를 애플리케이션 400 에러코드로 일관 변환하는 흐름이 PR 목적에 부합합니다. 트랜잭션 롤백도 유지됩니다.
| try { | ||
| GithubRepoUrl repoUrl = new GithubRepoUrl(request.url()); | ||
| GithubRepository savedRepository = saveRepository(repoUrl, user); | ||
| webHookService.registerWebhook(user, savedRepository.getId()); | ||
| eventPublisher.publishEvent(new AnalyzeMyPrEvent(repoUrl, user, this)); | ||
| return savedRepository; | ||
| } catch (GithubNotFoundException githubNotFoundException) { | ||
| throw new GssException(ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND); | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
원인 예외 보존 또는 로깅 고려
운영 트러블슈팅을 위해 원인 예외를 GssException에 첨부하거나 최소 로그로 남기는 것을 권장합니다. GssException이 cause를 받는 생성자/팩토리를 제공한다면 다음 형태가 바람직합니다: new GssException(ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND, githubNotFoundException)
다음 스크립트로 GssException 생성자 시그니처를 확인해 주세요:
가능 시 적용 예:
- } catch (GithubNotFoundException githubNotFoundException) {
- throw new GssException(ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND);
+ } catch (GithubNotFoundException githubNotFoundException) {
+ // cause를 보존하거나 적어도 로그로 남기는 것을 권장
+ throw new GssException(ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND, githubNotFoundException);
}🏁 Script executed:
#!/bin/bash
# GssException 생성자/팩토리 확인
rg -nP -C3 '\bclass\s+GssException\b'
rg -nP -C2 'GssException\s*\('
rg -nP -C2 'of\s*\(\s*ErrorCode'Length of output: 32063
GssException에 원인 예외를 전달할 수 있는 생성자 추가 및 적용 필요
현재 GssException은 ErrorCode만 받는 생성자만 제공되어 원인 예외(cause)를 보존할 수 없습니다. 로깅 또는 추후 디버깅을 위해 아래와 같이 수정이 필요합니다.
수정 사항:
gss-common/src/main/java/com/devoops/exception/custom/GssException.java에Throwable원인 예외를 받는 생성자 추가gss-api-app/src/main/java/com/devoops/service/facade/RepositoryFacadeService.java등 예외를 던지는 모든 곳에 해당 생성자를 사용하여 cause를 전달
예시 코드:
// gss-common/src/main/java/com/devoops/exception/custom/GssException.java
public class GssException extends RuntimeException {
private final ErrorCode errorCode;
public GssException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}
// 원인 예외 보존용 생성자 추가
public GssException(ErrorCode errorCode, Throwable cause) {
super(errorCode.getMessage(), cause);
this.errorCode = errorCode;
}
// getter...
}// gss-api-app/src/main/java/com/devoops/service/facade/RepositoryFacadeService.java
- } catch (GithubNotFoundException githubNotFoundException) {
- throw new GssException(ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND);
+ } catch (GithubNotFoundException githubNotFoundException) {
+ // 원인 예외를 보존하여 추후 로깅/디버깅에 활용
+ throw new GssException(ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND, githubNotFoundException);
}- 대상 파일 및 위치
• gss-common/src/main/java/com/devoops/exception/custom/GssException.java
• gss-api-app/src/main/java/com/devoops/service/facade/RepositoryFacadeService.java: 라인 36–44 외 예외 처리부 전반
위 리팩터링을 통해 원인 예외가 손실되지 않고, 운영 중 발생한 오류를 보다 정확히 파악할 수 있습니다.
🤖 Prompt for AI Agents
In
gss-api-app/src/main/java/com/devoops/service/facade/RepositoryFacadeService.java
around lines 36–44 and in
gss-common/src/main/java/com/devoops/exception/custom/GssException.java, add a
GssException constructor that accepts (ErrorCode, Throwable) and use it when
rethrowing from the catch block so the original GithubNotFoundException is
preserved as the cause; update GssException to call
super(errorCode.getMessage(), cause) and set the errorCode field, then replace
throw new GssException(ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND) with
throw new GssException(ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND,
githubNotFoundException) (and apply same pattern to other locations that rethrow
GssException).
🚩 연관 JIRA 이슈
close #72
🔂 변경 내역
Github에서는 잘못된 url이거나 토큰의 접근권한이 없는 레포지토리로부터 웹훅 심기, 정보 얻기 등의 api를 호출하면 일관적으로 404 NOT_FOUND를 반환합니다.
{ "message": "Not Found", "documentation_url": "https://docs.github.com/rest/repos/webhooks#create-a-repository-webhook", "status": "404" }이에 따라 POST /api/repositories에서 권한 문제가 발생할 경우, OAuth APP 리다이렉트를 통해 접근권한을 허용하는 플로우를 가져가고자 다음과 같은 cusom errorcode를 통해
등록에 실패한 상황을 구분합니다.{ "code": "REGISTRY_GITHUB_REPOSITORY_NOT_FOUND", "status": "BAD_REQUEST", "message": "입력한 깃허브 레포지토리를 등록할 수 없습니다" }🗣️ 리뷰 요구사항 (선택)
Summary by CodeRabbit
버그 수정
테스트
작업 정리(Chores)